# -*- coding: utf-8 -*-
from typing import Optional, List, Tuple

from fastapi import FastAPI, Query, Path, Body, File, UploadFile, HTTPException, Request
from pydantic import Field, conint, HttpUrl, AnyHttpUrl
from pydantic.main import BaseModel
from uuid import UUID

# from starlette.requests import Request
# from starlette.responses import JSONResponse
from starlette import status
from starlette.responses import JSONResponse

app = FastAPI()


# 查询参数
# tags, 标签分类，在api文档中将api分类
@app.get("/m", tags=["params"])
# 1. None也可指定默认值，None为非必须值，为必须值时使用 ...代替None
# async def many(s: list = Query(None)):
# 2. 指定参数长度
# async def many(s: str = Query(None, min_length=2, max_length=5)):
#     return {"s": s}
# 3. 正则
# 4. 描述
#   description，在/docs中描述参数
#   deprecated=True，改参数已弃用，但某些客户端仍在使用，使用此参数说明已弃用
# 5. 别名alias, 路由参数展示别名 ?a-q=xx&s=xx
async def many(q: str = Query(None, alias="a-q"),
               s: str = Query(..., min_length=2, max_length=5, regex="^\d+$",
                              description="need num str",
                              title="query string",
                              deprecated=True,
                              )):
    return {"s": s, "a": q}


# async def many(s: Optional[List[str]] = Query(None)):
# 查询字符串出现多次，?s=aa&s=bb&s=cc
# 也可以直接使用list，代替[List[str]]，此种情况不会检查类型
# 指定默认值
# async def many(s: Optional[List[str]] = Query(["start", "end"])):


# 路径参数
@app.get("/a/{uid}", tags=["params"])
# async def index(uid: int, s=None):
# 1. 路径参数都是必需的，只能用... Path内参数同Query
# async def index(uid: int = Path(..., gt=0),
#                 s=None):
# 2. 必传查询参数，没有默认参数，不用Query指定None，可以使用*，
async def index(*, uid: int = Path(..., gt=0), s: str):
    return {"uid": uid, "s": s}


# 请求体
class Body1(BaseModel):
    name: str = "fast"
    age: int
    gender: Optional[int] = 0
    # tags: List[str] = []
    # tags: Set[str] = []
    # tags: Tuple[str] = []


@app.post("/p", tags=["body"])
# async def a(body: Body1):
# 嵌套单个请求体
async def a(body: Body1 = Body(..., ys=True)):
    # print(body.name)
    return body
    # 此处不能链式调用
    # data = body.dict()
    # data.update(phone="131511")
    # # print(data["name"])
    # return data


# 多个请求体
class Body2(BaseModel):
    # Field参数同Query，Path和Body，example字段在文档中例子使用
    phone: str = Field(..., min_length=11, max_length=11, example="13122222222")
    # phone: str = Field(max_items=11)
    address: str = Field(None, min_length=11, max_length=11, example="省市区")


# {
#     "b1": {
#         "age": 11
#     },
#     "b2": {
#         "phone": "11356",
#         "address": "哈哈"
#     },
#     "only": 1
# }
@app.post("/b")
# async def mb(b1: Body1, b2: Body2):
#     res = {"b1": b1, "b2": b2}
# 添加单个请求体,Body参数同Query和Path
async def mb(b1: Body1, b2: Body2, only: int = Body(...)):
    res = {"b1": b1, "b2": b2, "only": only}
    return res


# 嵌套模型
class Book(BaseModel):
    name: str
    author: str
    user: Optional[Body1] = None
    img: HttpUrl


@app.post("/pp")
async def book(b: Book):
    return b


@app.post("/f/")
async def create_file(file: bytes = File(...)):
    return {"file_size": len(file)}


@app.post("/uf/")
async def create_upload_file(file: UploadFile = File(...)):
    d = {
        "filename": file.filename,
        "fct": file.content_type,
        "ff": file.file,
        "fr": len(await file.read())  # 和file.file互斥
    }
    return d


# 多文件
@app.post("/fs")
async def create_file(files: List[bytes] = File(...)):
    return [{"file_size": len(file)} for file in files]


@app.post("/ufs")
async def create_upload_file(files: List[UploadFile] = File(...)):
    d = [{
        "filename": file.filename,
        "fct": file.content_type,
        "ff": file.file,
        "fr": len(await file.read())  # 和file.file互斥
    } for file in files]
    return d


@app.post("/ff")
async def create_upload_file(request: Request, file1: bytes = File(...), file2: UploadFile = File(...)):
    d = {
        "file1_len": len(file1),
        "filename": file2.filename,
        "fct": file2.content_type,
        "ff": file2.file,
        "fr": len(await file2.read())  # 和file.file互斥
    }
    # 异常处理
    raise HTTPException(status_code=200,
                        detail="url:{}, base_url:{}, scope:{}, path: {} client:{}".format(
                            request.url, request.base_url,  request.scope,
                            request.scope["path"], request.client.host))
    # return d


# 自定义异常
class MyException(Exception):
    def __init__(self, name: str, msg: str):
        self.name = name
        self.msg = msg


@app.exception_handler(MyException)
async def unicorn_exception_handler(request: Request, exc: MyException):
    return JSONResponse(
        status_code=status.HTTP_400_BAD_REQUEST,
        content={"status": status.HTTP_400_BAD_REQUEST,
                 "message": "Path: {}, Error Message: {}.".format(exc.name, exc.msg)},
    )


@app.post("/er")
async def create_upload_file(request: Request):
    raise MyException(name=request.scope["path"], msg="error")
